import 'dart:io';
import 'dart:math';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

/// 下载文件,支持断点续传、断网重连后自动恢复下载
class FileDownloader {
  final Dio _dio;
  int _alreadyDownloaded = 0;
  int _totalFileSize = 0;
  String _downloadUrl = '';
  String _savePath = '';
  CancelToken _cancelToken = CancelToken();

  FileDownloader()
      : _dio = Dio(BaseOptions(
          validateStatus: (status) => status! < 500, // 允许4xx状态码
          headers: {'Accept-Encoding': 'identity'}, // 避免gzip干扰进度
        ));

  /// 主下载方法
  Future<void> download({
    required String url,
    required String fileName,
    required void Function(double progress) onProgress,
    required void Function(String path) onComplete,
    required void Function(String error, int retryCount) onError,
    int maxRetries = 3,
  }) async {
    int retryCount = 0;
    _downloadUrl = url;

    while (retryCount < maxRetries) {
      try {
        await _prepareDownload(fileName);
        await _executeDownload(onProgress, onComplete);
        return;
      } on DioException catch (e) {
        retryCount++;
        if (e.response?.statusCode == 416) {
          await _handle416Error();
          continue; // 自动重试
        }
        onError('下载失败: ${e.message}', retryCount);
        await Future.delayed(Duration(seconds: 1)); // 延迟重试
      } catch (e) {
        onError('未知错误: ${e.toString()}', retryCount);
        break;
      }
    }
  }

  Future<void> _prepareDownload(String fileName) async {
    final dir = await getApplicationDocumentsDirectory();
    _savePath = '${dir.path}/$fileName';
    _cancelToken = CancelToken();

    print('_savePath:$_savePath');
    final file = File(_savePath);
    if (await file.exists()) {
      _alreadyDownloaded = await file.length();
      print('发现已存在文件，大小: ${_formatSize(_alreadyDownloaded)}');

      // 关键修复：强制验证文件完整性
      await _fetchTotalFileSize();
      if (_totalFileSize > 0 && _alreadyDownloaded == _totalFileSize) {
        if (await _verifyFileIntegrity(file)) {
          print('文件已验证完整，直接使用');
          return;
        } else {
          print('文件不完整或已损坏，重新下载');
          await file.delete();
          _alreadyDownloaded = 0;
        }
      }
    } else {
      _alreadyDownloaded = 0;
    }

    // 重新获取最新文件大小
    await _fetchTotalFileSize();
  }

  /// 验证文件完整性（可选：SHA256校验）
  Future<bool> _verifyFileIntegrity(File file) async {
    try {
      // 方案1：简单大小验证
      if (_totalFileSize > 0) {
        return await file.length() == _totalFileSize;
      }

      // 方案2：哈希校验（需服务端提供）
      // final serverHash = await _getServerFileHash();
      // final localHash = await _calculateFileHash(file);
      // return localHash == serverHash;

      return false;
    } catch (e) {
      return false;
    }
  }

  /// 获取远程文件大小
  Future<void> _fetchTotalFileSize() async {
    try {
      final response = await _dio.head(_downloadUrl);
      _totalFileSize = int.parse(
        response.headers.value('content-length') ?? '0',
      );

      print('_totalFileSize: $_totalFileSize');
      print('_alreadyDownloaded: $_alreadyDownloaded');
      // 备用方案：通过Range请求检测
      if (_totalFileSize <= 0) {
        final rangeResponse = await _dio.get(
          _downloadUrl,
          options: Options(
            responseType: ResponseType.stream,
            headers: {'Range': 'bytes=0-0'},
          ),
        );
        final contentRange = rangeResponse.headers.value('content-range');
        _totalFileSize = int.parse(contentRange?.split('/').last ?? '0');
        print('通过Range请求获取文件大小: $_totalFileSize');
      }
    } catch (e) {
      print('获取文件大小失败: $e');
      _totalFileSize = 0; // 未知大小模式
    }
  }

  /// 执行下载核心逻辑
  Future<void> _executeDownload(void Function(double progress) onProgress,
      Function(String path) onComplete) async {
    // 检查是否已下载完成
    if (_totalFileSize > 0 && _alreadyDownloaded == _totalFileSize) {
      print('文件已完整，跳过下载');
      return;
    }

    // 配置Range请求
    if (_alreadyDownloaded > 0) {
      _dio.options.headers['Range'] = 'bytes=$_alreadyDownloaded-';
    }

    final file = File(_savePath);
    final fileStream = file.openWrite(mode: FileMode.append);

    try {
      final response = await _dio.get(
        _downloadUrl,
        options: Options(
          responseType: ResponseType.stream,
          receiveDataWhenStatusError: true,
        ),
        cancelToken: _cancelToken,
      );

      // 处理重定向后的真实大小
      if (_totalFileSize <= 0) {
        _totalFileSize = int.parse(
              response.headers.value('content-length') ?? '0',
            ) +
            _alreadyDownloaded;
      }

      int received = 0;
      final stopwatch = Stopwatch()..start();

      await response.data.stream.listen(
        (chunk) async {
          fileStream.add(chunk);
          received += int.parse(chunk.length.toString());
          _updateProgress(received, onProgress, stopwatch);
        },
        onDone: () async {
          await fileStream.close();
          stopwatch.stop();
          print('下载完成，耗时: ${stopwatch.elapsed}');
          onComplete(_savePath);
        },
        onError: (e) async {
          print('下载出错: $e');
          cancel();
          await fileStream.close();
          throw e;
        },
        cancelOnError: true,
      );
    } catch (e) {
      print('下载出错: $e');
      cancel();
      await fileStream.flush();
      await fileStream.close();
      rethrow;
    }
  }

  /// 处理416错误
  Future<void> _handle416Error() async {
    print('处理416 Range Not Satisfiable错误...');
    final file = File(_savePath);

    // 方案1：验证文件是否已完整
    if (_totalFileSize > 0 && await file.length() == _totalFileSize) {
      print('文件实际已完整，跳过下载');
      return;
    }

    // 方案2：删除文件重新下载
    if (await file.exists()) {
      await file.delete();
      _alreadyDownloaded = 0;
      _dio.options.headers.remove('Range');
      print('已删除可能损坏的文件，将重新下载');
    }
  }

  /// 更新进度
  void _updateProgress(
    int received,
    void Function(double progress) onProgress,
    Stopwatch stopwatch,
  ) {
    final totalReceived = received + _alreadyDownloaded;
    double progress;

    if (_totalFileSize > 0) {
      progress = totalReceived / _totalFileSize;
    } else {
      // 未知总大小模式
      progress = -1; // 用负数表示不确定进度
    }

    // 计算下载速度
    final speed = stopwatch.elapsedMilliseconds > 0
        ? totalReceived / stopwatch.elapsedMilliseconds * 1000
        : 0;

    print(
      progress >= 0
          ? '${_formatSize(totalReceived)}/${_formatSize(_totalFileSize)} '
              '${(progress * 100).toStringAsFixed(1)}% '
              '${_formatSize(speed.toInt())}/s'
          : '已下载: ${_formatSize(totalReceived)} '
              '${_formatSize(speed.toInt())}/s',
    );

    if (progress >= 0) {
      onProgress(progress.clamp(0.0, 1.0));
    }
  }

  /// 取消下载
  void cancel() {
    _cancelToken.cancel('用户取消');
  }

  /// 格式化文件大小
  static String _formatSize(int bytes) {
    if (bytes <= 0) return '0B';
    const suffixes = ['B', 'KB', 'MB', 'GB'];
    final i = (log(bytes) / log(1024)).floor();
    return '${(bytes / pow(1024, i)).toStringAsFixed(1)}${suffixes[i]}';
  }
}
